package heap

import "lesson01/classfile"

type Method struct {
	ClassMember
	maxStack     uint //操作数栈大小
	maxLocals    uint //局部变量表大小
	code         []byte
	argSlotCount uint
}

func newMethods(class *Class, cfMethods []*classfile.MemberInfo) []*Method {
	methods := make([]*Method, len(cfMethods))
	for i, cfMethod := range cfMethods {
		methods[i] = newMethod(class, cfMethod)
	}
	return methods
}

func newMethod(class *Class, cfMethod *classfile.MemberInfo) *Method {
	method := &Method{}
	method.class = class
	method.copyMemberInfo(cfMethod)
	method.copyAttributes(cfMethod)
	md := parseMethodDescriptor(method.descriptor)
	method.calcArgSlotCount(md.parameterTypes)

	if method.IsNative() {
		method.injectCodeAttribute(md.returnType)
	}

	return method
}

// copyAttributes 复制属性
func (e *Method) copyAttributes(cfMethod *classfile.MemberInfo) {
	if codeAttr := cfMethod.CodeAttribute(); codeAttr != nil {
		e.maxStack = codeAttr.MaxStack()
		e.maxLocals = codeAttr.MaxLocals()
		e.code = codeAttr.Code()
	}

}

func (e *Method) calcArgSlotCount(paramTypes []string) {
	for _, paramType := range paramTypes {
		e.argSlotCount++
		if paramType == "J" || paramType == "D" {
			e.argSlotCount++
		}
	}

	if !e.IsStatic() {
		e.argSlotCount++
	}

}

func (e *Method) injectCodeAttribute(returnType string) {
	e.maxStack = 4
	e.maxLocals = e.argSlotCount
	switch returnType[0] {
	case 'V':
		e.code = []byte{0xfe, 0xb1} //return
	case 'L', '[':
		e.code = []byte{0xfe, 0xb0} //areturn
	case 'D':
		e.code = []byte{0xfe, 0xaf} //dreturn
	case 'F':
		e.code = []byte{0xfe, 0xae} //freturn
	case 'J':
		e.code = []byte{0xfe, 0xad} //lreturn
	default:
		e.code = []byte{0xfe, 0xac} //ireturn
	}
}

//region gets

func (e *Method) MaxStack() uint {
	return e.maxStack
}

func (e *Method) MaxLocals() uint {
	return e.maxLocals
}

func (e *Method) Code() []byte {
	return e.code
}

func (e *Method) ArgSlotCount() uint {
	return e.argSlotCount
}

//endregion

//region 判断

func (e *Method) IsSynchronized() bool {
	return e.accessFlags&ACC_SYNCHRONIZED != 0
}

func (e *Method) IsBridge() bool {
	return 0 != e.accessFlags&ACC_BRIDGE
}

func (e *Method) IsVarargs() bool {
	return 0 != e.accessFlags&ACC_VARARGS
}

func (e *Method) IsNative() bool {
	return 0 != e.accessFlags&ACC_NATIVE
}

func (e *Method) IsAbstract() bool {
	return 0 != e.accessFlags&ACC_ABSTRACT
}

func (e *Method) IsStrict() bool {
	return 0 != e.accessFlags&ACC_STRICT
}

//endregion
